/*
 ============================================================================
 Name        : cpu.c
 Author      : Fahad Muzaffar
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 Modified by : Tarl Hahn 6/6/2013
 ============================================================================
 */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

#include "global.h"
#include "intCtrl.h"
#include "devices.h"
#include "process.h"
#include "SharedMem.h"
#include "scheduler.h"

#define Thousand 1000
#define MAX 10000

//prototypes
int createProcess(ProcessPtr* array, int num_of_process);
int typeGenerate(int* requests, int request);
int IOlist(int* list, int reqs);
int UIlist(int* list, int reqs);
int procConsumerlist(int* list, int reqs);

int loadScheduler(SchedulerPtr this, int num)
{	// check for null pointer
	if (this == (SchedulerPtr) NULL) { return NULL_PTR; }
	// get array of processes
	ProcessPtr* array = (ProcessPtr*) malloc(sizeof(ProcessPtr) * num);
	createProcess(array, num);
	int i;
	for(i = 0; i < num; i++)
	{	// fill scheduler with processes
		this->addProcess(this, array[i]);
	}
	return NO_ERROR;
}

char* getInterrupt(int interrupt)
{
	char* array = (char*) malloc(sizeof(char)* 20);
	switch(interrupt)
	{
		case NETWORK:
			strcpy(array, "IO Interrupt");
			return array;
		case DISK_IO:
			strcpy(array,"IO Interrupt");
			return array;
		case VIDEO:
			strcpy(array,"IO Interrupt");
			return array;
		case MOUSE:
			strcpy(array,"User Interrupt");
			return array;
		case KEYBOARD:
			strcpy(array,"User Interrupt");
			return array;
	}
}

int CPURun(int num_proc, int done, SharedMemPtr mem)
{
												//temp process variable
	SchedulerPtr scheduler = SchedulerConstruct(100, ROUND_ROBIN, FCFS);
	 
	int Check;									// Check for CONTINUE_PROCESS, DEAD_PROCESS and									
	int interrupt;								//	TIMER INTERRUPT, IO INTERRUPT, MUTEX INTERRUPT, CONDITION VAR INTERRUPT
	int index;									// Run Loop
	int interrupt_ids[] = {0, NETWORK}; 		// {pid, did}										
	int error;									// Check for errors
	
	loadScheduler(scheduler, num_proc);			// loads the scheduler with processes
	ProcessPtr proc = scheduler->nextProcess(scheduler, &error); // gets the next process from the schedueler
	
	pthread_mutex_t lock; 				//mutex lock variable
	pthread_mutex_init(&lock, NULL);    //initialize mutex lock
	// cpu loop lasting until index == done.
	for(index = 0; index < done; index++)
	{
		usleep(10000);
		interrupt = mem->getInterrupt(mem, &lock, &interrupt_ids[0]);			// get an interrupt if available
		switch(interrupt)
		{
			case TIMER_INTERRUPT:
				printf("\nTimer Interrrupt\n");
				if(proc->pid !=IDLE)
				{
					printf("Process %d returned to ReadyQueue\n", proc->pid);
					scheduler->addProcess(scheduler, proc);
				}
				proc = scheduler->nextProcess(scheduler, &error); // gets the next process from the schedueler

			break;

			case EMPTY:
			break;

			default: //interept
				printf("Interrupt: %d\n", interrupt);
				scheduler->proc_Interrupt(scheduler, interrupt_ids[0], interrupt_ids[1], &error);
		}
		
		if (proc->pid != IDLE)
		{
			Check = proc->updateProc(proc);
			switch(Check)
			{
				case CONTINUE_PROCESS:
				break;
			
				case DEAD_PROCESS:
					scheduler->addProcess(scheduler, proc);
					proc = scheduler->nextProcess(scheduler, &error);
				break;
				default: // TIMER INTERRUPT, IO INTERRUPT, MUTEX INTERRUPT, CONDITION VAR INTERRUPT
					printf("\nProcess %d: System Call\n", proc->pid);
					
					if (Check < MUTEX_REQ) {mem->addRequest(mem, proc->pid, Check, &lock);}
					
					proc->waiting_on = Check;
					error = scheduler->addProcess(scheduler, proc);
					printf("\nUpdated Blocked Queues\n");
					printf(scheduler->toStringQueues(scheduler, &error));
					proc = scheduler->nextProcess(scheduler, &error);
					printf("\nProcess %d, Now Running\n", proc->pid);
					
			}	
			if(error == EMPTY)
			{
				printf("Idling.. No Processes to run\n", proc->pid);
				if (scheduler->mutex != (ProcessPtr) NULL)
				{
					proc = scheduler->mutex;
					scheduler->mutex = scheduler->mutex->next;
				} else if(scheduler->cond != (ProcessPtr) NULL)
				{
					proc = scheduler->cond;
					scheduler->cond = scheduler->cond->next;
				}
			
			}else
			{
				printf("Process %d Now Running\n", proc->pid);
			}
		} else
		{
			scheduler->addProcess(scheduler, proc);
			proc = scheduler->nextProcess(scheduler, &error);	
		}
		
	}

	return 0;
}

int createProcess(ProcessPtr* array, int num) //needs to return the address of first element of array
{	
	int i, error = 0;
	int priority, type, steps, request = 0; // initialize variables
	int* requests;	// requests pointer
	// random process creations
	for(i=0; i < num; i++)
	{
		priority = (rand() % 5) + 1;//1-5 random
		steps = (rand() % 100) + 1;
		request = steps / 10;
		type = typeGenerate(requests, request);

		array[i] = ProcessConstruct(i+1, priority, type, steps, request, NULL);
	}
	return NO_ERROR;
}// end of createProcess

int typeGenerate(int* requests, int request)
{
	int type;
	int typeGen = (rand()%5)+1;// generates random number 1-5.
	
	switch(typeGen)
	{
		case 1:
			return COMPUTE;
			
		case 2:
			return IO;
		
		case 3:
			return UI;
			
		case 4:
			return PRODUCER;

		case 5:
			return CONSUMER;
	}
}

int main(int argc, char *argv[])
{
	int opt;
	int proc = 20;
	int steps = 100;
	int compute = 4;
	int io = 3;
	int ui = 3;
	int producer_pair = 5;
	while (opt = getopt(argc, argv, "pciuts") != -1)
	{
		switch (opt)
		{
		case 'p': // # processes
			proc = atoi(optarg);
			break;
		case 'c':
			compute = atoi(optarg);
			break;
		case 'i':
			io = atoi(optarg);
			break;
		case 'u':
			ui = atoi(optarg);
			break;
		case 't':
			producer_pair = atoi(optarg);
			break;
		case 's':
			steps = atoi(optarg);
			break;
    }
}
	printf("Scheduler Sim\n");
	
	SharedMemPtr mem = SharedMemConstruct();
	intCtrlPtr ctrl;

	int deviceArray[] = {1, 2, 3, 5};
	ctrl = intCtrl_init(mem, deviceArray, 4);
	CPURun(proc, steps, mem);
	mem->run_flag = 0;
	pthread_join(ctrl->thread, NULL);
	return 0;
}
